第一次聽到Pattern,我以為是設計模式,難以理解為什麼都是pattern好像在講兩件事。這邊的模式意謂資料的型態,FP的函數需要關注的是輸入的資料格式,然後用適當的方法處裡成結果,以昨天介紹的Option為例就是一種可為空的資料格式,再看一下程式片段
public static Option Map<TSource, TResult>(this Option source, Func<TSource, TResult> operate)
{
return source switch
{
None => source,
Some<TSource> { Data: var sourceData } =>
Option.Some(operate(sourceData)),
_ => throw new ArgumentException()
};
}
如果資料的型態為None,就會回傳None型態的結果,如果是Some,就須將Some這個容器內部的資料拿出來做處裡。函數式程式設計為了方便資料處裡,發展出了模式比對,把關注點放在資料的形狀上,C#7以後加入了很多的語法糖,如果從物件導向的觀點其實不太能理解這些模式要怎麼應用,接下來會一一介紹。
用於判斷某個類型是否相容於另一個類型,如果相容則同時做轉型
int? number = null;
if (number is int message) Console.WriteLine(message);
//相當於
int? number = null;
if (number != null) Console.WriteLine(number.Value);
這邊可以回來看最上面的switch expression,其實就是宣告模式搭配var與屬性模式的用法,switch expression可以視為早期switch-case的變形,但這種寫法其實是函數式設計的風格,每一個case其實是省略is關鍵字的宣告,而透過var拿出Option裡面存儲的資料,如果對應的資料是某個類別的屬性,就可以針對該屬性取值,另外要注意的是,使用var的話會相容null,而使用明確宣告像是int或是string的話,遇到null判斷的結果則為否。
static string Classify(double measurement) => measurement switch
{
< -4.0 => "Too low",
> 10.0 and <20.0 => "little high",
double.NaN => "Unknown",
_ => "Acceptable",
};
C#也支援< -4.0
這樣的判斷比對(關聯式),並且利用 not
、 or
、and
連接陳述式(邏輯),最後的_
則表示不在意這個值(捨棄)
var point = new Point { X = 10, Y = 60 };
var (x, y) = point;
其實也可以用屬性的位置取值,將類別成員解構
這個功能預計在C#11的時候推出,對array跟list有更方便的操作,終於把
var array = new int[] { 1, 2, 3, 4, 5, 6 };
array is [var x, ..var y];
// x = 1
// y = [2,3,4,5,6]
var range = 1..3;
array is [var a, range, ..var b];
// a = 1
// b = [5,6]
第一次看到這個的時候可能覺得莫名其妙,但是這個功能對函數式風格真的超級好用阿!
今天簡單介紹了C#中的pattern match語法,可以參考微軟文件有各種範例。如果單純把這些功能當成語法糖,其實使用起來有點雞肋,因為物件導向的設計比較少會遇到需要對類別做解構的場景,會比較專注於物件本事具有的行為,並且將資料封裝在物件內部,而函數式的設計幾乎是直接將資料結構暴露在外面,然後用解構的語法取出所需的資料,這樣的角度思考可能比較能夠體會pattern match,明天預計再次回到map方法然後介紹高階函數,希望能夠講到IEnumerable與linq。